/*
UP3: Command Shell

The system should have a command shell that can do at least the following:

    * Start processes. For example, it shall be possible to start another interpreter.
    * Change priority of processes.
    * Obtain information about present processes.
    * Terminate processes.
    * Ouput to the Malta LCD display
    * It should be possible to have more than one shell running simultaneously.
    * The command shell should not be special-treated by the operating system in any way.
    * The command shell has to handle "backspace" functionality when entering commands.
    * The command shell should not use CPU time when waiting for input.
*/

#include<stdlib.h>
#include<stdio.h> 

#include "shell.h"

/*commands have a fixed structure to make this simpler:
  cmdName param1 param2 param3 ... paramN
  SPACE (Ascii 32) serves as separator between them
*/

char line[MAX_CMD_LEN]; //command has to be shorter than one line

/*
Remark: This is ugly and hardcoded buit I don't want to steal code from a real shell here because that would be far too sophisticated.
*/
int recg_cmd(char* line){

  if(porn_strncmp_space(line, cmd_0, MAX_CMD_LEN) == 0)
    return CMD_EXIT_SHELL;

  if(porn_strncmp_space(line, cmd_1, MAX_CMD_LEN) == 0)
    return CMD_START_PROC;
  
  if(porn_strncmp_space(line, cmd_2, MAX_CMD_LEN) == 0)
    return CMD_CHANGE_PRIORITY;

  if(porn_strncmp_space(line, cmd_3, MAX_CMD_LEN) == 0)
    return CMD_GET_INFO;

  if(porn_strncmp_space(line, cmd_4, MAX_CMD_LEN) == 0)
    return CMD_TERMINATE;

  if(porn_strncmp_space(line, cmd_5, MAX_CMD_LEN) == 0)
    return CMD_ECHO;
  
  if(porn_strncmp_space(line, cmd_6, MAX_CMD_LEN) == 0)
    return CMD_ECHO_MALTA;
  
  if(porn_strncmp_space(line, cmd_7, MAX_CMD_LEN) == 0)
    return CMD_HELP;

  return CMD_NOT_RECOGNIZED;

}

/*
These are the functions that execute the specific behavior. Here most changes have to be made to adapt this to PORN-OS
*/

void exit_shell(){
  //This one is simple
  exit(EXIT_SUCCESS);
}


void start_proc(char* line){
  
  /*We need to do some more command parsing start expects a command
    name and up to 4 more numerical argumentsm that will be written
    into the a registers on application startup.*/

  //find out the app_name
  char app_name[MAX_CMD_LEN];
  char param1[MAX_CMD_LEN];
  char param2[MAX_CMD_LEN];
  char param3[MAX_CMD_LEN];
  char param4[MAX_CMD_LEN];

  //The parameters are 0 by default
  int iparam1 = 0;
  int iparam2 = 0;
  int iparam3 = 0;
  int iparam4 = 0;

  //check if user entered a space after start
  if(line[5] != '\x20'){
    printf("start: malformed input parameters, no sp\n");
    return;
  }
   

  //copy the app name from the space after 'start' to the next space
  porn_memccpy(app_name, line+6, MAX_CMD_LEN-6);

  int len_app_name = porn_strlen(app_name);
  if(len_app_name == 0){
    printf("start: malformed input parameters, no app name\n");
    return;
  }

  //TODO: This is kind of ugly
  //Now there might be 0 - 4 optional parameters. They need to be parsed and casted to ints
  if(line[5+len_app_name+1] == '\x20'){
    
    //first parameter
    porn_memccpy(param1, line+len_app_name+7, MAX_CMD_LEN-6);
    
    int len_param1 = porn_strlen(param1);
    
    //check if 3rd space exists
    if(line[len_app_name+len_param1+7] != '\x20'){
      printf("start: malformed input parameters, numerical arg bad\n");
      return;
    }

    //second parameter
    porn_memccpy(param2, line+len_app_name+len_param1+8, MAX_CMD_LEN-6);
    
    int len_param2 = porn_strlen(param2);
    
    //check if 4th space exists
    if(line[len_app_name+len_param1+len_param2+8] != '\x20'){
      printf("start: malformed input parameters, numerical arg bad\n");
      return;
    }

    //third parameter
    porn_memccpy(param3, line+len_app_name+len_param1+len_param2+9, MAX_CMD_LEN-6);
    
    int len_param3 = porn_strlen(param3);
    
    //check if 5th space exists
    if(line[len_app_name+len_param1+len_param2+len_param3+9] != '\x20'){
      printf("start: malformed input parameters, numerical arg bad\n");
      return;
    }

    //fourth parameter
    porn_memccpy(param4, line+len_app_name+len_param1+len_param2+len_param3+10, MAX_CMD_LEN-6);
    
    int len_param4 = porn_strlen(param4);
    
    //after the 4. param we allow nothing but enter
    if(line[len_app_name+len_param1+len_param2+len_param3+len_param4+10] != '\xa'){
      printf("start: malformed input parameters, numerical arg bad\n");
      return;
    }
    
    //parse the parameters to ints
    iparam1 = porn_str2int(param1);
    iparam2 = porn_str2int(param2);
    iparam3 = porn_str2int(param3);
    iparam4 = porn_str2int(param4);

    if(
       iparam1 == -1 || 
       iparam2 == -1 || 
       iparam3 == -1 || 
       iparam4 == -1){
      
      printf("start: malformed input parameters, numerical arg bad\n");
      return;
    }

  }


  //execute the command
  int fork_res = fork();
  if(fork_res == 0){

    //execute the actual command
    int returnValue = execvp(app_name, NULL);

    //if the command cannot be executed, we tell the user
    if(returnValue || !returnValue){
      printf("command not found\n");
      exit(EXIT_FAILURE);
    }
  
  }else{ //wait for the termination of the child

    wait();

  }

}

void change_priority(char* line){

  //TODO: parse input, analog start_proc
  //TODO: this has no equivalent in the libc version
  printf("successfully done nothing\n");

}

void get_info(char* line){

  //TODO: parse input, analog start_proc
  //TODO: this has no equivalent in the libc version
  printf("successfully done nothing\n");

}

void terminate_proc(char* line){

  //TODO: parse input, analog start_proc
  //TODO: this has no equivalent in the libc version
  printf("successfully done nothing\n");

}

void echo(char* line){

  //I make this simple without "...", etc.
  if(line[4] == '\x20'){
    line += 5;
    

    printf("%s", line);

  }else
    return;

}

void echo_malta(char* line){

  //I make this simple without "...", etc.
  if(line[9] == '\x20'){
    line += 10;

    //we better remove the lf char to not confuse the malta device
    int i=0;
    while(1){
      if (line[i] == '\xa'){
	line[i] = '\x0';
	break;
      }
      i++;
    }

    printf("symbolic malta: %s\n", line);

  }else
    return;

}

void help(char* line){

  printf("PORN-OS Shell Help\nthe following commands are available:\nexit : exit the shell\nstart progname [param1 param2 param3] : start a program\npriority ... : change the priority of a process\nprocinfo ... : get information about another process\nkill ....... : kill a process\necho string  : print a string\nechomalta    : print a string to malta\nhelp         : display this help\n");

}

int main(int argc, char argv[]){

  int cmd_code = -1;
  do{

    printf("$:");
    fflush(NULL);

    /* Read a command line */
    if (!fgets(line, MAX_CMD_LEN, stdin)) 
      return EXIT_FAILURE;

    //TODO:strip a line into the single parts, without using libc functions
    cmd_code = recg_cmd(line);

    //take appropriate actions
    switch(cmd_code){

    case CMD_NOT_RECOGNIZED:
      {
	printf("unknown command\n");
	break;
      }
    case CMD_EXIT_SHELL:
      {
	exit_shell();
	break;
      }
    case CMD_START_PROC:
      {
	start_proc(line);
	break;
      }
    case CMD_CHANGE_PRIORITY:
      {
	change_priority(line);
	break;
      }
    case CMD_GET_INFO:
      {
	get_info(line);
	break;
      }
    case CMD_TERMINATE:
      {
	terminate_proc(line);
	break;
      }
    case CMD_ECHO:
      {
	echo(line);
	break;
      }
    case CMD_ECHO_MALTA:
      {
	echo_malta(line);
	break;
      }
    case CMD_HELP:
      {
	help(line);
	break;
      }
      
    };



  }while(cmd_code != CMD_EXIT_SHELL);

  return EXIT_SUCCESS;

}
